package com.cxy.common.util;

import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.ArrayList;

/*********************************************************
 * 文件名称：CIDRUtils.java
 * 系统名称：CXY自定义系统
 * 模块名称：com.cxy.common.util
 * 功能说明：CIDI工具类，提供了一些方法来计算 CIDR 地址的网络地址、广播地址以及判断一个 IP 地址是否在某个 CIDR 地址范围内。
 * 开发人员 @author：caoxy31978
 * 开发时间 @date：2023/6/29 14:36
 * 修改记录：程序版本  修改日期  修改人员  修改单号  修改说明
 *********************************************************/
public class CIDRUtils {
    private final String cidr;
    private InetAddress inetAddress;
    private InetAddress startAddress;
    private InetAddress endAddress;
    private final int prefixLength;

    public CIDRUtils(String cidr) throws UnknownHostException {
        this.cidr = cidr;
        if (this.cidr.contains("/")) {
            int index = this.cidr.indexOf("/");
            String addressPart = this.cidr.substring(0, index);
            String networkPart = this.cidr.substring(index + 1);
            this.inetAddress = InetAddress.getByName(addressPart);
            this.prefixLength = Integer.parseInt(networkPart);
            this.calculate();
        } else {
            throw new IllegalArgumentException("not an valid CIDR format!");
        }
    }

    private void calculate() throws UnknownHostException {
        ByteBuffer maskBuffer;
        byte targetSize;
        if (this.inetAddress.getAddress().length == 4) {
            maskBuffer = ByteBuffer.allocate(4).putInt(-1);
            targetSize = 4;
        } else {
            maskBuffer = ByteBuffer.allocate(16).putLong(-1L).putLong(-1L);
            targetSize = 16;
        }

        BigInteger mask = (new BigInteger(1, maskBuffer.array())).not().shiftRight(this.prefixLength);
        ByteBuffer buffer = ByteBuffer.wrap(this.inetAddress.getAddress());
        BigInteger ipVal = new BigInteger(1, buffer.array());
        BigInteger startIp = ipVal.and(mask);
        BigInteger endIp = startIp.add(mask.not());
        byte[] startIpArr = this.toBytes(startIp.toByteArray(), targetSize);
        byte[] endIpArr = this.toBytes(endIp.toByteArray(), targetSize);
        this.startAddress = InetAddress.getByAddress(startIpArr);
        this.endAddress = InetAddress.getByAddress(endIpArr);
    }

    private byte[] toBytes(byte[] array, int targetSize) {
        int counter = 0;

        ArrayList newArr;
        for(newArr = new ArrayList(); counter < targetSize && array.length - 1 - counter >= 0; ++counter) {
            newArr.add(0, array[array.length - 1 - counter]);
        }

        int size = newArr.size();

        for(int i = 0; i < targetSize - size; ++i) {
            newArr.add(0, (byte)0);
        }

        byte[] ret = new byte[newArr.size()];

        for(int i = 0; i < newArr.size(); ++i) {
            ret[i] = (Byte)newArr.get(i);
        }

        return ret;
    }

    public String getNetworkAddress() {
        return this.startAddress.getHostAddress();
    }

    public String getBroadcastAddress() {
        return this.endAddress.getHostAddress();
    }

    public boolean isInRange(String ipAddress) throws UnknownHostException {
        InetAddress address = InetAddress.getByName(ipAddress);
        BigInteger start = new BigInteger(1, this.startAddress.getAddress());
        BigInteger end = new BigInteger(1, this.endAddress.getAddress());
        BigInteger target = new BigInteger(1, address.getAddress());
        int st = start.compareTo(target);
        int te = target.compareTo(end);
        return (st == -1 || st == 0) && (te == -1 || te == 0);
    }
}
